/**
 * Copyright (c) 2013-2022 Nikita Koksharov
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson.config;

import io.netty.channel.EventLoopGroup;
import org.redisson.client.DefaultNettyHook;
import org.redisson.client.NettyHook;
import org.redisson.client.codec.Codec;
import org.redisson.codec.Kryo5Codec;
import org.redisson.connection.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 创建Redisson对象配置类
 */
public class Config {

    static final Logger log = LoggerFactory.getLogger(Config.class);

    // 哨兵模式
    private SentinelServersConfig sentinelServersConfig;
    // 主从模式
    private MasterSlaveServersConfig masterSlaveServersConfig;
    // 单机模式
    private SingleServerConfig singleServerConfig;
    // 集群模式
    private ClusterServersConfig clusterServersConfig;
    // 云托管模式
    private ReplicatedServersConfig replicatedServersConfig;



    // 连接管理
    private ConnectionManager connectionManager;


    /**
     * 线程池中线程的数量,默认是16
     * 这个变量决定了同时可以执行的任务数
     */
    private int threads = 16;

    /**
     * 定义netty使用的线程数,默认是32
     * 这个变量控制着Netty框架内部处理网络请求的线程池大小
     * 对于高并发的网络应用来说，这是一个非常重要的配置项
     */
    private int nettyThreads = 32;

    /**
     * 执行具体的编码或者解码任务
     */
    private Codec codec;

    /**
     * executor字段是一个ExecutorService实例，用于管理任务的执行。
     * ExecutorService是一个线程池服务，可以提交任务（Runnable或Callable）进行异步执行。
     * 它提供了多种控制机制，比如启动、关闭线程池，以及提交和执行任务的方法。
     * 本类中，executor将用于异步执行各种任务，提高程序的效率和并发能力。
     */
    private ExecutorService executor;

    /**
     * 参考启用状态的标志。
     * 该变量控制着某些功能是否启用的逻辑，初始设置为true，表示功能默认启用。
     */
    private boolean referenceEnabled = true;

    /**
     * 这个变量定义了传输模式，默认为NIO（非阻塞I/O）。
     * 变量类型为TransportMode枚举，决定了数据传输时使用的模式，
     * 可以是NIO（非阻塞I/O）或者其它的传输模式，具体取决于应用的需求和环境。
     */
    private TransportMode transportMode = TransportMode.NIO;

    /**
     * EventLoopGroup是Netty框架中用于处理I/O操作的关键组件。在NIO和异步I/O模型中，它扮演着线程池的角色，
     * 管理一系列的EventLoop（事件循环），每个EventLoop负责处理多个Channel（通道）的I/O事件。
     * 这个成员变量通常在服务端编程中用于接受和处理客户端的连接请求，执行网络读写等操作。
     */
    private EventLoopGroup eventLoopGroup;

    /**
     * 定义锁的看门狗超时时间，默认为30秒。
     * 这个变量用来设置锁等待的最长时间，超过这个时间，
     * 如果锁仍未被释放，则认为出现异常情况，需要进行相应的处理。
     * 单位为毫秒。
     */
    private long lockWatchdogTimeout = 30 * 1000;


    /**
     * 用于控制是否检查从属（slave）节点是否同步。
     * 其默认值为true，表示系统会检查从属节点是否与主节点同步。
     * 这个变量通常在涉及分布式系统一致性检查的场景中使用。
     */
    private boolean checkLockSyncedSlaves = true;


    /**
     * 设定slaves同步超时时间的默认值。
     * 该变量表示master节点等待slaves节点同步数据的最大时间，单位为毫秒。
     * 如果在指定时间内slaves未完成同步，将视为同步超时。
     * 默认值为1000毫秒。
     */
    private long slavesSyncTimeout = 1000;

    /**
     * 设置可靠主题的看门狗超时时间
     * 用于设置可靠主题的看门狗超时时间，默认值为10分钟。
     * 超时时间单位为毫秒。
     */
    private long reliableTopicWatchdogTimeout = TimeUnit.MINUTES.toMillis(10);

    /**
     * 控制是否保持发布订阅消息的顺序。
     * 默认值为true，表示需要保持消息的顺序。
     * 注意：此变量的更改可能会影响到消息的处理逻辑。
     */
    private boolean keepPubSubOrder = true;

    /**
     * 标记是否使用脚本缓存。
     * <p>该变量为私有成员变量，类型为boolean，默认值为false。在需要缓存脚本执行结果以提高性能的情况下，可以设置为true。</p>
     */
    private boolean useScriptCache = false;

    /**
     * minCleanUpDelay 属性定义了最小的清理延迟时间，单位通常为秒或毫秒，具体取决于应用程序的上下文和使用场景。
     * 这个属性用于控制或限制清理操作（如垃圾回收、资源释放等）的最小间隔时间。
     * 默认值为 5，表示在两次清理操作之间至少需要等待 5 秒（或毫秒）。
     */
    private int minCleanUpDelay = 5;

    /**
     * 最大清理延迟时间
     * 这个成员变量定义了系统进行清理操作的最大延迟时间，以秒为单位。默认值为30分钟（30*60秒）。
     * 该值用于控制某些需要定时清理的任务的最大延迟时间，确保系统的及时性。
     */
    private int maxCleanUpDelay = 30 * 60;

    /**
     * 定义清理键的数量。
     * 该变量用于指定在清理操作中要处理的键的数量。
     * 默认值为100。
     */
    private int cleanUpKeysAmount = 100;

    /**
     * 创建并初始化NettyHook实例。
     * NettyHook用于Netty相关的钩子操作，例如监控、日志记录等。
     * 这里使用的是DefaultNettyHook的默认实现。
     */
    private NettyHook nettyHook = new DefaultNettyHook();

    /**
     * 它用于监听连接相关的事件，具体实现和用途依赖于上下文中的具体业务逻辑。
     */
    private ConnectionListener connectionListener;

    /**
     * 该变量控制是否使用线程类加载器。
     * 默认值为true，表示在多线程环境下会使用线程类加载器。
     * 注意：更改此值可能影响到线程间类的加载与隔离。
     */
    private boolean useThreadClassLoader = true;

    /**
     * 这段代码声明了一个私有的AddressResolverGroupFactory实例，
     * 用于创建地址解析器组。具体实现为SequentialDnsAddressResolverFactory，
     * 表示它使用顺序DNS解析策略。
     */
    private AddressResolverGroupFactory addressResolverGroupFactory = new SequentialDnsAddressResolverFactory();

    /**
     * 控制是否启用懒加载初始化的标志。
     * 该变量为私有成员，通常用于在类的内部判断是否需要延迟初始化某些资源或数据。
     * @return boolean - 如果为true，则表示启用懒加载初始化；反之，如果为false，则表示不启用懒加载初始化。
     */
    private boolean lazyInitialization;


    public Config() {
    }

    public Config(Config oldConf) {
        setNettyHook(oldConf.getNettyHook());
        setExecutor(oldConf.getExecutor());

        if (oldConf.getCodec() == null) {
            // use it by default
            oldConf.setCodec(new Kryo5Codec());
        }

        setConnectionListener(oldConf.getConnectionListener());
        setUseThreadClassLoader(oldConf.isUseThreadClassLoader());
        setMinCleanUpDelay(oldConf.getMinCleanUpDelay());
        setMaxCleanUpDelay(oldConf.getMaxCleanUpDelay());
        setCleanUpKeysAmount(oldConf.getCleanUpKeysAmount());
        setUseScriptCache(oldConf.isUseScriptCache());
        setKeepPubSubOrder(oldConf.isKeepPubSubOrder());
        setLockWatchdogTimeout(oldConf.getLockWatchdogTimeout());
        setCheckLockSyncedSlaves(oldConf.isCheckLockSyncedSlaves());
        setSlavesSyncTimeout(oldConf.getSlavesSyncTimeout());
        setNettyThreads(oldConf.getNettyThreads());
        setThreads(oldConf.getThreads());
        setCodec(oldConf.getCodec());
        setReferenceEnabled(oldConf.isReferenceEnabled());
        setEventLoopGroup(oldConf.getEventLoopGroup());
        setTransportMode(oldConf.getTransportMode());
        setAddressResolverGroupFactory(oldConf.getAddressResolverGroupFactory());
        setReliableTopicWatchdogTimeout(oldConf.getReliableTopicWatchdogTimeout());
        setLazyInitialization(oldConf.isLazyInitialization());

        if (oldConf.getSingleServerConfig() != null) {
            setSingleServerConfig(new SingleServerConfig(oldConf.getSingleServerConfig()));
        }
        if (oldConf.getMasterSlaveServersConfig() != null) {
            setMasterSlaveServersConfig(new MasterSlaveServersConfig(oldConf.getMasterSlaveServersConfig()));
        }
        if (oldConf.getSentinelServersConfig() != null) {
            setSentinelServersConfig(new SentinelServersConfig(oldConf.getSentinelServersConfig()));
        }
        if (oldConf.getClusterServersConfig() != null) {
            setClusterServersConfig(new ClusterServersConfig(oldConf.getClusterServersConfig()));
        }
        if (oldConf.getReplicatedServersConfig() != null) {
            setReplicatedServersConfig(new ReplicatedServersConfig(oldConf.getReplicatedServersConfig()));
        }
        if (oldConf.getConnectionManager() != null) {
            useCustomServers(oldConf.getConnectionManager());
        }
    }

    public NettyHook getNettyHook() {
        return nettyHook;
    }

    public Config setNettyHook(NettyHook nettyHook) {
        this.nettyHook = nettyHook;
        return this;
    }

    public Config setCodec(Codec codec) {
        this.codec = codec;
        return this;
    }

    public Codec getCodec() {
        return codec;
    }

    public boolean isReferenceEnabled() {
        return referenceEnabled;
    }

    public void setReferenceEnabled(boolean redissonReferenceEnabled) {
        this.referenceEnabled = redissonReferenceEnabled;
    }

    public ClusterServersConfig useClusterServers() {
        return useClusterServers(new ClusterServersConfig());
    }

    ClusterServersConfig useClusterServers(ClusterServersConfig config) {
        checkMasterSlaveServersConfig();
        checkSentinelServersConfig();
        checkSingleServerConfig();
        checkReplicatedServersConfig();

        if (clusterServersConfig == null) {
            clusterServersConfig = config;
        }
        return clusterServersConfig;
    }

    
    
    
    
    protected ClusterServersConfig getClusterServersConfig() {
        return clusterServersConfig;
    }

    protected void setClusterServersConfig(ClusterServersConfig clusterServersConfig) {
        this.clusterServersConfig = clusterServersConfig;
    }

    public ReplicatedServersConfig useReplicatedServers() {
        return useReplicatedServers(new ReplicatedServersConfig());
    }

    ReplicatedServersConfig useReplicatedServers(ReplicatedServersConfig config) {
        checkClusterServersConfig();
        checkMasterSlaveServersConfig();
        checkSentinelServersConfig();
        checkSingleServerConfig();

        if (replicatedServersConfig == null) {
            replicatedServersConfig = new ReplicatedServersConfig();
        }
        return replicatedServersConfig;
    }

    protected ReplicatedServersConfig getReplicatedServersConfig() {
        return replicatedServersConfig;
    }

    protected void setReplicatedServersConfig(ReplicatedServersConfig replicatedServersConfig) {
        this.replicatedServersConfig = replicatedServersConfig;
    }


    @Deprecated
    ConnectionManager getConnectionManager() {
        return connectionManager;
    }


    @Deprecated
    public void useCustomServers(ConnectionManager connectionManager) {
        this.connectionManager = connectionManager;
    }


    /**
     * 单机模式创建对象
     */
    public SingleServerConfig useSingleServer() {
        return useSingleServer(new SingleServerConfig());
    }

    SingleServerConfig useSingleServer(SingleServerConfig config) {
        checkClusterServersConfig();
        checkMasterSlaveServersConfig();
        checkSentinelServersConfig();
        checkReplicatedServersConfig();
        if (singleServerConfig == null) {
            singleServerConfig = config;
        }
        return singleServerConfig;
    }


    protected SingleServerConfig getSingleServerConfig() {
        return singleServerConfig;
    }

    protected void setSingleServerConfig(SingleServerConfig singleConnectionConfig) {
        this.singleServerConfig = singleConnectionConfig;
    }


    public SentinelServersConfig useSentinelServers() {
        return useSentinelServers(new SentinelServersConfig());
    }

    SentinelServersConfig useSentinelServers(SentinelServersConfig sentinelServersConfig) {
        checkClusterServersConfig();
        checkSingleServerConfig();
        checkMasterSlaveServersConfig();
        checkReplicatedServersConfig();

        if (this.sentinelServersConfig == null) {
            this.sentinelServersConfig = sentinelServersConfig;
        }
        return this.sentinelServersConfig;
    }

    protected SentinelServersConfig getSentinelServersConfig() {
        return sentinelServersConfig;
    }

    protected void setSentinelServersConfig(SentinelServersConfig sentinelConnectionConfig) {
        this.sentinelServersConfig = sentinelConnectionConfig;
    }


    public MasterSlaveServersConfig useMasterSlaveServers() {
        return useMasterSlaveServers(new MasterSlaveServersConfig());
    }

    MasterSlaveServersConfig useMasterSlaveServers(MasterSlaveServersConfig config) {
        checkClusterServersConfig();
        checkSingleServerConfig();
        checkSentinelServersConfig();
        checkReplicatedServersConfig();

        if (masterSlaveServersConfig == null) {
            masterSlaveServersConfig = config;
        }
        return masterSlaveServersConfig;
    }

    protected MasterSlaveServersConfig getMasterSlaveServersConfig() {
        return masterSlaveServersConfig;
    }

    protected void setMasterSlaveServersConfig(MasterSlaveServersConfig masterSlaveConnectionConfig) {
        this.masterSlaveServersConfig = masterSlaveConnectionConfig;
    }

    public boolean isClusterConfig() {
        return clusterServersConfig != null;
    }

    public boolean isSentinelConfig() {
        return sentinelServersConfig != null;
    }

    public boolean isSingleConfig() {
        return singleServerConfig != null;
    }

    public int getThreads() {
        return threads;
    }


    public Config setThreads(int threads) {
        this.threads = threads;
        return this;
    }

    private void checkClusterServersConfig() {
        if (clusterServersConfig != null) {
            throw new IllegalStateException("cluster servers config already used!");
        }
    }

    private void checkSentinelServersConfig() {
        if (sentinelServersConfig != null) {
            throw new IllegalStateException("sentinel servers config already used!");
        }
    }

    private void checkMasterSlaveServersConfig() {
        if (masterSlaveServersConfig != null) {
            throw new IllegalStateException("master/slave servers already used!");
        }
    }

    private void checkSingleServerConfig() {
        if (singleServerConfig != null) {
            throw new IllegalStateException("single server config already used!");
        }
    }

    private void checkReplicatedServersConfig() {
        if (replicatedServersConfig != null) {
            throw new IllegalStateException("Replication servers config already used!");
        }
    }


    public Config setTransportMode(TransportMode transportMode) {
        this.transportMode = transportMode;
        return this;
    }

    public TransportMode getTransportMode() {
        return transportMode;
    }


    public Config setNettyThreads(int nettyThreads) {
        this.nettyThreads = nettyThreads;
        return this;
    }

    public int getNettyThreads() {
        return nettyThreads;
    }


    public Config setExecutor(ExecutorService executor) {
        this.executor = executor;
        return this;
    }

    public ExecutorService getExecutor() {
        return executor;
    }


    public Config setEventLoopGroup(EventLoopGroup eventLoopGroup) {
        this.eventLoopGroup = eventLoopGroup;
        return this;
    }

    public EventLoopGroup getEventLoopGroup() {
        return eventLoopGroup;
    }


    public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {
        this.lockWatchdogTimeout = lockWatchdogTimeout;
        return this;
    }

    public long getLockWatchdogTimeout() {
        return lockWatchdogTimeout;
    }


    public Config setCheckLockSyncedSlaves(boolean checkLockSyncedSlaves) {
        this.checkLockSyncedSlaves = checkLockSyncedSlaves;
        return this;
    }

    public boolean isCheckLockSyncedSlaves() {
        return checkLockSyncedSlaves;
    }


    public Config setKeepPubSubOrder(boolean keepPubSubOrder) {
        this.keepPubSubOrder = keepPubSubOrder;
        return this;
    }

    public boolean isKeepPubSubOrder() {
        return keepPubSubOrder;
    }

    /**
     * Used to switch between {@link io.netty.resolver.dns.DnsAddressResolverGroup} implementations.
     * Switch to round robin {@link io.netty.resolver.dns.RoundRobinDnsAddressResolverGroup} when you need to optimize the url resolving.
     *
     * @param addressResolverGroupFactory
     * @return config
     */
    public Config setAddressResolverGroupFactory(AddressResolverGroupFactory addressResolverGroupFactory) {
        this.addressResolverGroupFactory = addressResolverGroupFactory;
        return this;
    }

    public AddressResolverGroupFactory getAddressResolverGroupFactory() {
        return addressResolverGroupFactory;
    }




    public static Config fromYAML(String content) throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.fromYAML(content, Config.class);
    }


    public static Config fromYAML(InputStream inputStream) throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.fromYAML(inputStream, Config.class);
    }


    public static Config fromYAML(File file) throws IOException {
        return fromYAML(file, null);
    }

    public static Config fromYAML(File file, ClassLoader classLoader) throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.fromYAML(file, Config.class, classLoader);
    }


    public static Config fromYAML(URL url) throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.fromYAML(url, Config.class);
    }


    public static Config fromYAML(Reader reader) throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.fromYAML(reader, Config.class);
    }


    public String toYAML() throws IOException {
        ConfigSupport support = new ConfigSupport();
        return support.toYAML(this);
    }


    public Config setUseScriptCache(boolean useScriptCache) {
        this.useScriptCache = useScriptCache;
        return this;
    }

    public boolean isUseScriptCache() {
        return useScriptCache;
    }

    public int getMinCleanUpDelay() {
        return minCleanUpDelay;
    }


    public Config setMinCleanUpDelay(int minCleanUpDelay) {
        this.minCleanUpDelay = minCleanUpDelay;
        return this;
    }

    public int getMaxCleanUpDelay() {
        return maxCleanUpDelay;
    }


    public Config setMaxCleanUpDelay(int maxCleanUpDelay) {
        this.maxCleanUpDelay = maxCleanUpDelay;
        return this;
    }

    public int getCleanUpKeysAmount() {
        return cleanUpKeysAmount;
    }


    public Config setCleanUpKeysAmount(int cleanUpKeysAmount) {
        this.cleanUpKeysAmount = cleanUpKeysAmount;
        return this;
    }

    public boolean isUseThreadClassLoader() {
        return useThreadClassLoader;
    }


    public Config setUseThreadClassLoader(boolean useThreadClassLoader) {
        this.useThreadClassLoader = useThreadClassLoader;
        return this;
    }

    public long getReliableTopicWatchdogTimeout() {
        return reliableTopicWatchdogTimeout;
    }


    public Config setReliableTopicWatchdogTimeout(long timeout) {
        this.reliableTopicWatchdogTimeout = timeout;
        return this;
    }
    public ConnectionListener getConnectionListener() {
        return connectionListener;
    }


    public Config setConnectionListener(ConnectionListener connectionListener) {
        this.connectionListener = connectionListener;
        return this;
    }
    public long getSlavesSyncTimeout() {
        return slavesSyncTimeout;
    }


    public Config setSlavesSyncTimeout(long timeout) {
        this.slavesSyncTimeout = timeout;
        return this;
    }

    public boolean isLazyInitialization() {
        return lazyInitialization;
    }

    public Config setLazyInitialization(boolean lazyInitialization) {
        this.lazyInitialization = lazyInitialization;
        return this;
    }

    @Deprecated
    public static Config fromJSON(String content) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.fromJSON(content, Config.class);
    }

    @Deprecated
    public static Config fromJSON(InputStream inputStream) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.fromJSON(inputStream, Config.class);
    }

    @Deprecated
    public static Config fromJSON(File file, ClassLoader classLoader) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.fromJSON(file, Config.class, classLoader);
    }

    @Deprecated
    public static Config fromJSON(File file) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        return fromJSON(file, null);
    }

    @Deprecated
    public static Config fromJSON(URL url) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.fromJSON(url, Config.class);
    }

    @Deprecated
    public static Config fromJSON(Reader reader) throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.fromJSON(reader, Config.class);
    }

    @Deprecated
    public String toJSON() throws IOException {
        log.error("JSON configuration is deprecated and will be removed in future!");
        ConfigSupport support = new ConfigSupport();
        return support.toJSON(this);
    }
}
